﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace app
{
    class Response
    {
        IPEndPoint RemoteEndPoint;

        IPEndPoint ReturnEndPoint;

        byte[] Data = new byte[0];

        byte[] Address = new byte[0];

        byte[] OriginalRequest;

        RequestHeader Header;

        Response(IPEndPoint remoteEndPoint, byte[] request)
        {
            OriginalRequest = request;

            RemoteEndPoint = remoteEndPoint;
        }

        internal static void Process(object o)
        {
            Server.ThreadStartParams t = (Server.ThreadStartParams)o;

            Log.Add("Process", t.RemoteEndPoint, t.Request);

            Response res = new Response(t.RemoteEndPoint, t.Request);

            res.Process();
        }

        void Process()
        {
            Header = new RequestHeader(OriginalRequest);

            if (Header.Command == RequestCommand.None)
                ReturnEndPoint = Addresses.FromBytes(OriginalRequest);

            if (ReturnEndPoint == null)
                ReturnEndPoint = RemoteEndPoint;

            if (Header.Command == RequestCommand.GetPeer || Header.Command == RequestCommand.GetPeerFromKownPeer)
            {
                ProcessGetPeers();
                return;
            }

            if (Header.Command == RequestCommand.AddPeer)
            {
                Data = OriginalRequest.
                            Skip(Parameters.requestHeaderSize).
                            Take(OriginalRequest.Length - (Parameters.requestHeaderSize)).ToArray();

                ProcessAddPeers();

                return;
            }

            Address = OriginalRequest.Skip(Parameters.requestHeaderSize).Take(Parameters.addressSize).ToArray();

            if (Address.Length < Parameters.addressSize)
                return;

            Data = OriginalRequest.Skip(Parameters.requestHeaderSize + Parameters.addressSize).ToArray();

            if (Data.Length == 0)
            {
                if (ProcessGetData())
                    return;
            }
            else
                ProcessAddData();
        }

        void ProcessGetPeers()
        {
            int reg_size = Parameters.addressSize + Parameters.ipv4Addresssize + sizeof(Int16);

            Request.Send(RemoteEndPoint, command: RequestCommand.AddPeer, data: Program.localPeer.ToBytes().Take(reg_size).ToArray());

            return;

            Peer[] pp = Peers.GetPeers(Address);

            byte[] result;



            if (Header.Command == RequestCommand.GetPeer)
                result = new byte[Parameters.requestHeaderSize + ((pp.Length + 1) * reg_size)];
            else
                result = new byte[Parameters.requestHeaderSize + (pp.Length * reg_size)];

            int i = 0;

            RequestHeader.ToBytes(RequestCommand.AddPeer).CopyTo(result, 0);

            if (Header.Command == RequestCommand.GetPeer)
                Program.localPeer.ToBytes().Take(reg_size).ToArray().CopyTo(result, Parameters.requestHeaderSize + (i++ * reg_size));

            foreach (Peer p in pp)
                p.ToBytes().Take(reg_size).ToArray().CopyTo(result, Parameters.requestHeaderSize + (i++ * reg_size));

            Request.Send(RemoteEndPoint, data: result, command: RequestCommand.AddPeer);
        }

        void ProcessAddPeers()
        {
            Peers.AddRange(Data);
        }

        bool ProcessGetData()
        {
            byte[] data = LocalData.Get(Address);

            if (data == null)
            {
                Peer[] pp;

                if (Header.Command == RequestCommand.None)
                {
                    pp = Peers.GetPeers(Address, originEndPoint: ReturnEndPoint);

                    if (pp == null || pp.Length < 1)
                        return true;

                    if (Addresses.Compare(pp[0].Address, Address) > Addresses.Compare(Program.localPeer.Address, Address))
                        return true;

                    foreach (Peer p in pp)
                        Request.Send(
                            p,
                            address: Address,
                            header: new RequestHeader(this.ReturnEndPoint));
                }

                return true;
            }

            Request.Send(
                ReturnEndPoint,
                address: Address,
                data: data);

            return true;
        }

        void ProcessAddData()
        {
            LocalData.Add(Address, Data);
        }
    }
}